using System;
using System.Web;
using System.Linq;
using System.Net;

namespace GoodStuff.Net
{
	/// <summary>
	/// A utility class to filter and compare IP adresses.
	/// </summary>
	internal abstract class IPUtility
	{
		//Deny construction
		private IPUtility()
		{
		}

        /// <summary>
        /// Verifies if the allowedIP matches the UserHostAddres of the current HttpRequest
        /// </summary>
        /// <param name="allowedIPs">single IPaddress. </param>
        /// <returns></returns>
		public static bool VerifyIP(string allowedIPs)
		{
            if (string.IsNullOrEmpty(allowedIPs))
            {
                return false;
            }
			return VerifyIP(allowedIPs.Split(new char[] {',', ';', ' '}, StringSplitOptions.RemoveEmptyEntries));
		}

        /// <summary>
        /// Verifies if the UserHostAddres of the current HttpRequest matches at least one of the allowedIPs.
        /// </summary>
        /// <param name="allowedIPs">single IPaddress. </param>
        /// <returns></returns>
        public static bool VerifyIP(string[] allowedIPs)
		{
			if(allowedIPs == null || allowedIPs.Length == 0)
			{
				return false;
			}

            if (HttpContext.Current == null || HttpContext.Current.Request == null)
            {
                throw new Exception("There is no current HttpContext, so users hostaddress can't be determined.");
            }

            string requestIP = HttpContext.Current.Request.UserHostAddress;

            //If any of the allowedIPs matches the sourceIP (requestIP), then we return true for "Verified"
            return MatchAny(requestIP, allowedIPs);
		}

        /// <summary>
        /// Matches the sourceIP against the comparteTo-IP. Format can be "192.168.100.50" (exact IP) or "127.0.0.0/24" (IP with subnetmask - matches if the sourceIP falls within the given subnetmask)
        /// </summary>
        /// <param name="sourceIP">The source IPaddress. Format can be "192.168.100.50" (exact IP) or "127.0.0.0/24" (IP with subnetmask)</param>
        /// <param name="compareTo">The IPaddress (exact or with subnetmask) to compare to. Format can be "192.168.100.50" (exact IP) or "127.0.0.0/24" (IP with subnetmask)</param>
        /// <returns>a Boolean indicating whether the source IP matches the 'compareTo'-IPAddress or falls within the given IPaddress and subnetmask.</returns>
        public static bool Match(string sourceIP, string compareTo)
        {
            return MatchAny(sourceIP, new string[] { compareTo });
        }

        /// <summary>
        /// Matches the sourceIP against an array of "comparteTo-IP's". Format can be "192.168.100.50" (exact IP) or "127.0.0.0/24" (IP with subnetmask - matches if the sourceIP falls within the given subnetmask)
        /// </summary>
        /// <param name="sourceIP">The source IPaddress. Format can be "192.168.100.50" (exact IP) or "127.0.0.0/24" (IP with subnetmask)</param>
        /// <param name="compareTo">The IPaddress (exact or with subnetmask) to compare to. Format can be "192.168.100.50" (exact IP) or "127.0.0.0/24" (IP with subnetmask)</param>
        /// <returns>a Boolean indicating whether the source IP matches at least one of the 'compareTo'-IPAddress or falls within the given IPaddress and subnetmask.</returns>
        public static bool MatchAny(string sourceIP, string[] compareTo)
        {
            IPAddress sourceAddress = IPAddress.Parse(sourceIP);
            foreach (string address in compareTo)
            {
                if (address.Contains("/"))
                {
                    string[] fragments = address.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
                    IPAddress ip = IPAddress.Parse(fragments[0]);
                    int subnet = int.Parse(fragments[1]);

                    if (sourceAddress.IsInSameSubnet(ip, SubnetMask.CreateByHostBitLength(subnet)))
                    {
                        return true;
                    }
                }
                else
                {
                    IPAddress ip = IPAddress.Parse(address);
                    if (sourceAddress.Equals(ip))
                    {
                        return true;
                    }
                }
            }

            return false; 
        }
	}
}
